using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Diagnostics.Contracts;
using System.Globalization;
using System.Linq;
using System.Web.UI;
using System.Web.UI.WebControls;
using Newtonsoft.Json;
using OpenWaves.Web;

namespace OpenWaves.ActivityFeed.Web.Controls
{
    public class ActivityFeedBase : WebControl
    {
        public ActivityFeedBase() : base(HtmlTextWriterTag.Div)
        {
            this.FeedType = Web.Controls.FeedType.Personal;
            this.TopicsFilter = new Collection<Topic>();
            this.NewTopics = new Collection<Topic>();
            this.PageSize = PaginationRequest.DefaultPageSize;
            this.UpdatesSettings = new ListClientSettings();
            this.CommentsSettings = new ListClientSettings();
            this.TopicsFilterSettings = new ListClientSettings();
        }

        [PersistenceMode(PersistenceMode.InnerProperty)]
        public PlaceHolder Template { get; set; }

        private readonly Collection<UpdateContentTemplate> updateContentTemplates = new Collection<UpdateContentTemplate>();
        [PersistenceMode(PersistenceMode.InnerProperty)]
        public Collection<UpdateContentTemplate> UpdateContentTemplates
        {
            get { return this.updateContentTemplates; }
        }

        [PersistenceMode(PersistenceMode.InnerProperty)]
        public KnockoutJSTemplate CommentTemplate { get; set; }

        [PersistenceMode(PersistenceMode.InnerProperty)]
        public KnockoutJSTemplate UpdateTemplate { get; set; }

        public FeedType FeedType { get; set; }

        public string CustomFeedTypeName { get; set; }

        #region control settings

        [Category("Activity Feed")]
        public Collection<Topic> TopicsFilter { get; private set; }

        [Category("Activity Feed")]
        public Collection<Topic> NewTopics { get; private set; }

        [Category("Activity Feed")]
        public Topic TopicFilter
        {
            get { return !this.TopicsFilter.Any() ? null : this.TopicsFilter[0]; }
            set
            {
                this.TopicsFilter.Clear();
                this.TopicsFilter.Add(value);
            }
        }

        [Category("Activity Feed")]
        public int PageSize { get; set; }

        [Category("Activity Feed")]
        public string OnActivityFeedViewModelInitialize { get; set; }

        [Category("Activity Feed")]
        public string OnUpdateViewModelInit { get; set; }

        #endregion

        #region viewModel events

        [Category("Activity Feed - client events")]
        public ListClientSettings UpdatesSettings { get; set; }

        [Category("Activity Feed - client events")]
        public ListClientSettings CommentsSettings { get; set; }

        [Category("Activity Feed - client events")]
        public ListClientSettings TopicsFilterSettings { get; set; }

        #endregion

        public string ScriptsPlaceholderControlId { get; set; }

        public IActivityFeedOptionsDtoBuilder ActivityFeedOptionsDtoBuilder { get; set; }

        public override void DataBind()
        {
            base.DataBind();
            this.CreateControlsHierarchy(true);
            this.ChildControlsCreated = true;
        }

        protected override void CreateChildControls()
        {
            this.CreateControlsHierarchy(false);
        }

        protected virtual void CreateControlsHierarchy(bool dataBinding)
        {
            Contract.Assert(this.Template != null, "Template must be specified for the ActivityFeed control to render.");
            this.Controls.Add(this.Template);
            
            Contract.Assert(this.UpdateTemplate != null, "Update Template must be specified for the ActivityFeed control to render.");
            this.Controls.Add(this.UpdateTemplate);

            if (dataBinding)
            {
                this.Template.DataBind();
                this.UpdateTemplate.DataBind();
            }

            foreach(var updateContentTemplate in this.UpdateContentTemplates)
            {
                this.Controls.Add(updateContentTemplate);
                if (dataBinding)
                    updateContentTemplate.DataBind();
            }

            if (this.CommentTemplate != null)
            {
                this.Controls.Add(this.CommentTemplate);
                if (dataBinding)
                    this.CommentTemplate.DataBind();
            }

            this._scriptContent = null;
            if (!string.IsNullOrEmpty(this.ScriptsPlaceholderControlId))
            {
                var scriptPlaceholder = this.Page.FindControlRecursive(this.ScriptsPlaceholderControlId);
                Contract.Assert(scriptPlaceholder != null,
                                string.Format(CultureInfo.InvariantCulture, "Cannot find script control: '{0}'", this.ScriptsPlaceholderControlId));
                this._scriptContent  = new Literal();
                scriptPlaceholder.Controls.Add(this._scriptContent);
            }
        }

        private Literal _scriptContent;

        protected override void OnPreRender(EventArgs e)
        {
            base.OnPreRender(e);

            if (this.ActivityFeedOptionsDtoBuilder == null)
            {
                this.ActivityFeedOptionsDtoBuilder = ServiceLocator.Resolve<IActivityFeedOptionsDtoBuilder>();
            }

            var optionsDto = this.ActivityFeedOptionsDtoBuilder.BuildActivityFeedOptionsDto(
                this.FeedType, 
                this.CustomFeedTypeName,
                this.TopicsFilter, 
                this.NewTopics, 
                this.PageSize, 
                this.updateContentTemplates,
                this.OnActivityFeedViewModelInitialize,
                this.OnUpdateViewModelInit);

            var declarationScript = String.Format(CultureInfo.InvariantCulture, 
                    @"var {0} = new activityFeed.ActivityFeedViewModel({1});", 
                    this.ClientID, 
                    Serialize(optionsDto));

            var initializationScript = String.Format(CultureInfo.InvariantCulture,
                                                     @"{0}.initialize(); ko.applyBindings({0}, $('#{0}')[0]);",
                                                     this.ClientID);
           
            if (this._scriptContent == null)
            {
                this.Page.ClientScript.RegisterClientScriptBlock(
                    typeof (ActivityFeed),
                    "declaration",
                    declarationScript,
                    true);

                this.Page.ClientScript.RegisterStartupScript(
                    typeof (ActivityFeed),
                    "initialization",
                    initializationScript,
                    true);
            }
            else
            {
                this._scriptContent.Text = string.Format(CultureInfo.InvariantCulture, "<script type='text/javascript'>//<![CDATA[\r\n\r\n{0}\r\n{1}\r\n//]]></script>",
                                                         declarationScript, initializationScript);
            }
        }

        private static string Serialize(object value)
        {
            return JsonConvert.SerializeObject(value, Formatting.Indented, JsonNetSerializerSettings.Current);
        }
    }
}